package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.asserts.Asserts;
import com.atguigu.srb.base.response.ResponseEnum;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.LendNoUtils;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendReturnMapper;
import com.atguigu.srb.core.service.*;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 还款记录表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2023-05-26
 */
@Service
@Slf4j
public class LendReturnServiceImpl extends ServiceImpl<LendReturnMapper, LendReturn> implements LendReturnService {
    @Resource
    UserAccountService userAccountService;
    @Resource
    UserInfoService userInfoService;
    @Resource
    LendItemReturnService lendItemReturnService;
    @Resource
    LendService lendService;
    @Resource
    LendItemService lendItemService;
    @Resource
    TransFlowService transFlowService;
    @Override
    public void updateLendReturns(List<LendReturn> lendReturns, List<LendItemReturn> lendItemReturns) {
        //保存了回款计划后 再更新所有还款计划的本金利息 本息
        lendReturns.forEach(lendReturn -> {
            //使用正在遍历的还款计划id查找自己所有的回款计划
            BigDecimal totalPrincipal = lendItemReturns.stream()
                    .filter(lendItemReturn -> lendItemReturn.getLendReturnId().equals(lendReturn.getId())) //数据过滤
                    .map(LendItemReturn::getPrincipal)//获取lendItemReturn集合的Principal属性值的集合
                    .reduce((a,b)-> a.add(b))//对Principal属性值的集合中的元素累加计算
                    .get();//获取计算结果
            BigDecimal totalInterest = lendItemReturns.stream()
                    .filter(lendItemReturn -> lendItemReturn.getLendReturnId().equals(lendReturn.getId())) //数据过滤
                    .map(LendItemReturn::getInterest)//获取lendItemReturn集合的Principal属性值的集合
                    .reduce((a,b)-> a.add(b))//对Principal属性值的集合中的元素累加计算
                    .get();//获取计算结果
            BigDecimal total = lendItemReturns.stream()
                    .filter(lendItemReturn -> lendItemReturn.getLendReturnId().equals(lendReturn.getId())) //数据过滤
                    .map(LendItemReturn::getTotal)//获取lendItemReturn集合的Principal属性值的集合
                    .reduce((a,b)-> a.add(b))//对Principal属性值的集合中的元素累加计算
                    .get();//获取计算结果
            //本金
            lendReturn.setPrincipal(totalPrincipal);
            //利息
            lendReturn.setInterest(totalInterest);
            //本息
            lendReturn.setTotal(total);
        });
        //批量更新
        this.updateBatchById(lendReturns);
    }

    @Override
    public List<LendReturn> saveLendReturns(Lend lend) {
        List<LendReturn> lendReturns = new ArrayList<>();
        for (Integer i = 1; i <= lend.getPeriod(); i++) {
            LendReturn lendReturn = new LendReturn();
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            //还款批次号：
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            //借款人id
            lendReturn.setUserId(lend.getUserId());
            //标的预期金额
            lendReturn.setAmount(lend.getAmount());
            //标的实际募资到的总金额：计算利息使用
            lendReturn.setBaseAmount(lend.getInvestAmount());
            lendReturn.setCurrentPeriod(i);
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setReturnMethod(lend.getReturnMethod());

            //判断如果还款方式为4，只需要最后一期设置本金利息
//            if(lend.getReturnMethod().equals(4)){
//                //最后一期设置值，否则设置0
//                if(i.equals(lend.getPeriod())){
//                    // 本金：
//                    lendReturn.setPrincipal(perMonthPrincipal.get(1));
//                    // 利息
//                    lendReturn.setInterest(perMonthInterest.get(1));
//                }else{
//                    // 本金：
//                    lendReturn.setPrincipal(new BigDecimal(0));
//                    // 利息
//                    lendReturn.setInterest(new BigDecimal(0));
//                }
//
//            }else{//还款方式为1，2，3
//                // 本金：
//                lendReturn.setPrincipal(perMonthPrincipal.get(i));
//                // 利息
//                lendReturn.setInterest(perMonthInterest.get(i));
//            }
            // 本息
//            lendReturn.setTotal(lendReturn.getPrincipal().add(lendReturn.getInterest()));

            // 投资人投资回报提现时可以收取手续费
            lendReturn.setFee(new BigDecimal(0));
            // 本期应还款日期: 从起息日开始 下个月的这一天就是第一期的还款日期
            lendReturn.setReturnDate(new DateTime(lend.getLendStartDate()).plusMonths(i).toDate());
            //是否逾期和逾期金额
            lendReturn.setOverdue(false);
            lendReturn.setOverdueTotal(new BigDecimal(0));
            //是否为最后一期
            lendReturn.setLast(i.equals(lend.getPeriod())?true:false);
            //默认状态：未归还
            lendReturn.setStatus(0);

            lendReturns.add(lendReturn);
        }
        this.saveBatch(lendReturns);
        return lendReturns;
    }

    @Override
    public List<LendReturn> listByLendId(String lendId) {
        return  this.list(Wrappers.lambdaQuery(LendReturn.class)
                .eq(LendReturn::getLendId,lendId));
    }

    @Override
    public List<LendReturn> listBorrowerLendReturns(String token) {
        Long userId = JwtUtils.getUserId(token);



        return baseMapper.selectBorrowerLendReturnsByUserId(userId);
    }

    @Override
    public String commitReturn(String id, String token) {
        Long userId = JwtUtils.getUserId(token);
        //    1、检查还款计划状态： 必须让用户按照还款期数顺序来还款
        UserInfo userInfo = userInfoService.getById(userId);
        //查询待还款的 按照期数升序排列的集合  然后查询第一条：最近需要还款的记录
        LendReturn lendReturn = this.getOne(Wrappers.lambdaQuery(LendReturn.class)
                .eq(LendReturn::getUserId, userId)
                .eq(LendReturn::getStatus, 0)
                .orderByAsc(LendReturn::getCurrentPeriod)
                .last("limit 1"));
        //当前用户没有待还款记录 或者  提交的还款计划id并不是最近的需要还款的记录
        Asserts.assertNotTrue(lendReturn==null || !lendReturn.getId().toString().equals(id), ResponseEnum.ERROR);
        //    2、检查借款人账户余额：
        BigDecimal accountAmount = userAccountService.getAmount(token);
        // 逾期：当前业务没有考虑逾期(hfb业务中未考虑)
        Asserts.assertNotTrue(accountAmount.compareTo(lendReturn.getTotal())<0 ,
                ResponseEnum.DRAW_AMOUNT_ERROR);
        //    3、构建自动提交的表单
        //             让浏览器访问hfb的还款接口 进行还款
        Lend lend = lendService.getById(lendReturn.getLendId());

        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        //拼接商品名称：
        params.put("agentGoodsName",lend.getTitle()+"第"+lendReturn.getCurrentPeriod()+"期");
        // 必须在transFlow表中唯一：表示本地唯一的交易   除了NO 我们也可以设置其他唯一的值
        //例如：一个还款只能还一次   使用它的id当做交易流水编号
//        String no = LendNoUtils.getNo();
        params.put("agentBatchNo", lendReturn.getReturnNo());
        //还款人的hfb bindCode
        params.put("fromBindCode",userInfo.getBindCode());
        params.put("totalAmt",lendReturn.getTotal());
        params.put("voteFeeAmt",new BigDecimal(0));
        params.put("returnUrl",HfbConst.BORROW_RETURN_RETURN_URL);
        params.put("notifyUrl",HfbConst.BORROW_RETURN_NOTIFY_URL);
        params.put("timestamp",System.currentTimeMillis());
        //还款明细: 本次还款对应的投资人们的投资回款账户等信息
        //本次还款 可以根据自己的id在回款计划表中查询回款计划
        List<LendItemReturn> lendItemReturns = lendItemReturnService.list(Wrappers.lambdaQuery(LendItemReturn.class)
                .eq(LendItemReturn::getLendReturnId, id));
        List<Map<String,Object>> data = lendItemReturns.stream().map(lendItemReturn -> {
            Map<String,Object> map = new HashMap<>();
            //回款对应的标的编号：
            map.put("agentProjectCode",lend.getLendNo());
            //本地回款计划对应的 投资记录的 编号
            LendItem lendItem = lendItemService.getById(lendItemReturn.getLendItemId());
            map.put("voteBillNo",lendItem.getLendItemNo());
            //投资人hfb账户
            UserInfo investUserInfo = userInfoService.getById(lendItemReturn.getInvestUserId());
            map.put("toBindCode",investUserInfo.getBindCode());
            //总金额
            map.put("transitAmt",lendItemReturn.getTotal());
            //本金
            map.put("baseAmt",lendItemReturn.getPrincipal());
            //利息
            map.put("benifitAmt",lendItemReturn.getInterest());
            //配置手续费: 月化率*收益*1   10
            BigDecimal feeAmt = new BigDecimal(10).divide(new BigDecimal(100), 8, BigDecimal.ROUND_DOWN)
                    .multiply(lendItemReturn.getInterest());
            map.put("feeAmt",feeAmt);
            return map;
        }).collect(Collectors.toList());
        String jsonArr = JSON.toJSONString(data);
        System.out.println(jsonArr);
        //需要传入一个json数组：json数组中每一个元素是一个回款信息
        params.put("data", jsonArr);
        params.put("sign", RequestHelper.getSign(params));
        String form = FormHelper.buildForm(HfbConst.BORROW_RETURN_URL, params);
        //    4、保存交易流水日志
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());
        transFlow.setUserName(userInfo.getName());
        // 交易流水号，可以使用本次交易的唯一编号设置
        transFlow.setTransNo(lendReturn.getReturnNo());
        transFlow.setTransType(8);
        transFlow.setTransTypeName("还款");
        transFlow.setTransAmount(lendReturn.getTotal());
        transFlow.setStatus(0);
        transFlowService.save(transFlow);

        return form;
    }

    @Override
    public String notifyUrl(HttpServletRequest request) {
        //获取参数
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
        //验证签名
        if(!RequestHelper.isSignEquals(map)){
            log.error("还款失败，签名错误：{}",JSON.toJSON(map));
            return "fail";
        }
        //验证状态码
        if(!StringUtils.equals("0001",map.get("resultCode").toString())){
            log.error("还款失败，hfb业务错误：{}",JSON.toJSON(map));
            return "fail";
        }
        //保证幂等性、校验还款金额
        String agentBatchNo = map.get("agentBatchNo").toString();
        TransFlow transFlow = transFlowService.getTransFlowByAgentBillNo(agentBatchNo);

        if(transFlow==null ){
            log.error("还款失败，非法请求：{}",JSON.toJSON(map));
            return "fail";
        }
        BigDecimal transAmount = transFlow.getTransAmount();
        if(transAmount.compareTo(new BigDecimal(map.get("totalAmt").toString()))!=0){
            log.error("还款失败，金额错误：{}",JSON.toJSON(map));
            return "fail";
        }
        if(transFlow.getStatus()!=0){
            return "success";
        }

        //还款成功：更新本地数据库数据
        //4.1 更新借款人账户：扣除还款金额
        //查询还款人账户信息
        Long userId = transFlow.getUserId();
        //UserInfo userInfo = userInfoService.getById(userId);
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));
        userAccount.setAmount(userAccount.getAmount().subtract(transAmount));
        userAccountService.updateById(userAccount);

        //4.2 更新还款计划的状态
        // agentBatchNo值之前存入时使用的是lendReturn的编号，所以可以基于它查询到还款计划
        LendReturn lendReturn = this.getOne(Wrappers.lambdaQuery(LendReturn.class)
                .eq(LendReturn::getReturnNo, agentBatchNo));
        lendReturn.setStatus(1);
        lendReturn.setOverdue(false);
        lendReturn.setOverdueTotal(new BigDecimal(0));
        lendReturn.setRealReturnTime(new Date());
        this.updateById(lendReturn);
        //保存还款人账户扣减日志
        TransFlow accountTransFlow = new TransFlow();
        accountTransFlow.setUserId(lendReturn.getUserId());
        //获取投资人会员数据
        UserInfo userInfo = userInfoService.getById(userId);
        accountTransFlow.setUserName(userInfo.getName());
        // 交易流水号，可以使用本次交易的唯一编号设置
        accountTransFlow.setTransNo(LendNoUtils.getNo());
        accountTransFlow.setTransType(9);
        accountTransFlow.setTransTypeName("还款扣款");
        accountTransFlow.setTransAmount(lendReturn.getTotal());
        accountTransFlow.setStatus(1);
        transFlowService.save(accountTransFlow);
        //4.3 更新回款计划的状态
        // 根据还款计划的id可以查询到自己的一组回款计划
        List<LendItemReturn> lendItemReturns = lendItemReturnService.list(Wrappers.lambdaQuery(LendItemReturn.class)
                .eq(LendItemReturn::getLendReturnId, lendReturn.getId())
                .eq(LendItemReturn::getStatus, 0));
        //遍历，更新每一个回款计划的状态，同时更新投资人账户余额
        lendItemReturns.forEach(lendItemReturn -> {
            //更新遍历中的回款计划的状态
            lendItemReturn.setRealReturnTime(new Date());
            lendItemReturn.setStatus(1);
            lendItemReturn.setOverdue(false);
            lendItemReturn.setOverdueTotal(new BigDecimal(0));
            lendItemReturnService.updateById(lendItemReturn);
            //更新回款计划的投资人账户金额: 上面因为扣除了手续费  所以此处回款时也要扣掉手续费  //4.2 更新投资人账户：入账
            UserAccount investUserAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                    .eq(UserAccount::getUserId, lendItemReturn.getInvestUserId()));
            BigDecimal feeAmt = new BigDecimal(10).divide(new BigDecimal(100), 8, BigDecimal.ROUND_DOWN)
                    .multiply(lendItemReturn.getInterest());
            investUserAccount.setAmount(investUserAccount.getAmount().add(lendItemReturn.getTotal().subtract(feeAmt)));
            userAccountService.updateById(investUserAccount);
            //保存交易日志
            TransFlow investTransFlow = new TransFlow();
            investTransFlow.setUserId(lendItemReturn.getInvestUserId());
            //获取投资人会员数据
            UserInfo investUserInfo = userInfoService.getById(lendItemReturn.getInvestUserId());
            investTransFlow.setUserName(investUserInfo.getName());
            // 交易流水号，可以使用本次交易的唯一编号设置
            investTransFlow.setTransNo(LendNoUtils.getNo());
            investTransFlow.setTransType(4);
            investTransFlow.setTransTypeName("投资回款");
            investTransFlow.setTransAmount(lendItemReturn.getTotal().subtract(feeAmt));
            log.info("回款金额明细，总回款：{},实际回款：{},手续费：{}",
                    lendItemReturn.getTotal(),lendItemReturn.getTotal().subtract(feeAmt),feeAmt);
            investTransFlow.setStatus(1);
            transFlowService.save(investTransFlow);
        });
        //更新上一步保存的还款人还款日志

        transFlow.setMemo(JSON.toJSONString(map));
        transFlow.setStatus(1);
        transFlowService.updateById(transFlow);
        return "success";
    }


}
